import { Client } from "ssh2";
import * as fs from "fs";
import * as path from "path";
import { remotePath, root } from "./config";
import { randomBytes } from "crypto";

export const ssh = {
  con: undefined as Client | undefined,
  shell: defShell, upFile: defUpFile,
  f(result: string) { console.log("命令执行完毕", result); }, s: "", e: "", d: ""
};
// 生成一个标准的 UUID v4
function generateUUIDv4() {
  return ("10000000" + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, (c) => {
    const n = Number(c);
    // @ts-ignore
    return (n ^ randomBytes(1)[0] & 15 >> n / 4).toString(16);
  }
  );
}
const nfs: cbk[] = [];
setInterval(() => { nfs.splice(0).forEach(f => { try { f(); } catch (_) { } }); }, 100);
function next() { return new Promise<void>((resolve) => { nfs.push(resolve); }); }
export function runSSH() {
  return new Promise<void>((resolve, reject) => {
    if (ssh.con) {
      return resolve();
    }
    const sshConfig = {
      host: process.env.svrIP,
      port: parseInt(process.env.sshPort || "22") || 22,
      username: process.env.sshUsername,
      privateKey: process.env.sshKey || fs.readFileSync(path.resolve((process.env.sshKeyFile || '~/.ssh/id_rsa').replace(/^~/, process.env.HOME || process.env.USERPROFILE || "")), "utf8"),
    },
      conn = new Client();
    conn.on("ready", () => {
      ssh.con = conn;
      conn.shell((err, stream) => {
        if (err) {
          reject();
          return console.error("创建shell错误:", err);
        }
        ssh.shell = (commond) => {
          return new Promise<string>(async (resolve) => {
            while (ssh.e) { await next(); }
            console.log("执行ssh命令:", commond);
            ssh.d = "";
            ssh.e = generateUUIDv4();
            const t = commond.replace("\r", "").split("\n");
            while (t.length) {
              if ((ssh.s = t.pop() || "")) {
                break;
              }
            }
            commond += `\necho ${ssh.e}\n`;
            ssh.f = resolve;
            stream.write(commond);
          });
        };
        stream.on("data", (data: Buffer) => {
          if (ssh.e) {
            ssh.d += data.toString();
            if (new RegExp(`^${ssh.e}$`, "m").test(ssh.d)) {
              ssh.e = "";
              let s = ssh.d, i = s.indexOf(ssh.s) + ssh.s.length;
              if (i >= 0) {
                const j = s.search(new RegExp(`.*echo ${ssh.e}`, "m"));
                s = s.substring(i, j > i ? j : undefined);
              }
              try { s = JSON.parse(s); } catch (_) { }
              ssh.f(s);
            }
          } else {
            ssh.d = "";
          }
        }).stderr.on("data", (data) => {
          console.error("shell错误:", data);
        });
      });
      conn.sftp((err, sftp) => {
        if (err) {
          reject();
          return console.error("创建sftp错误:", err);
        }
        ssh.upFile = (fullPath: string, remoteFilePath?: string) => {
          return new Promise<void>((resolve, reject) => {
            try {
              if (!remoteFilePath) {
                remoteFilePath = remotePath + fullPath.substring(root.length).replace(/\\/g, "/");
              }
              const
                readStream = fs.createReadStream(fullPath),
                writeStream = sftp.createWriteStream(remoteFilePath);
              fs.stat(fullPath, (err, stat) => {
                if (err) {
                  console.log("文件上传失败:", fullPath, err);
                } else if (stat.size > 1024 * 1024) {
                  let uploadedSize = 0, l = 0;
                  const h = setInterval(() => {
                    l = 1;
                    process.stdout.clearLine(-1);
                    process.stdout.cursorTo(0);
                    process.stdout.write(`文件上传中: ${fullPath}(${Math.floor(uploadedSize / stat.size * 100)}%) ${uploadedSize} / ${stat.size}`);
                  }, 100);
                  readStream.on("data", chunk => {
                    uploadedSize += chunk.length;
                    // console.log("文件上传中:", fullPath, uploadedSize, stat.size);
                    writeStream.write(chunk, err => {
                      if (err) {
                        console.log("文件上传失败:", fullPath, err);
                      }
                    });
                  });
                  readStream.on("end", () => {
                    l && console.log("");
                    clearInterval(h);
                    writeStream.end();
                  });
                } else {
                  readStream.pipe(writeStream);
                }
              });
              writeStream.on("close", () => {
                console.log("文件上传成功:", fullPath);
                readStream.destroy();
                writeStream.destroy();
                resolve();
              });
              writeStream.on("error", (err: any) => {
                console.log("文件上传失败:", fullPath, err);
                reject(err);
              });
            } catch (error) {
              console.error("发生其他错误", error);
              reject(error);
            }
          });
        };
        resolve();
      });
    }).connect(sshConfig);
    conn.on("error", (err) => {
      console.log("SSH连接失败", err);
      ssh.con = undefined;
      ssh.shell = defShell;
      ssh.upFile = defUpFile;
      reject();
    });
    conn.on("end", () => {
      console.log("SSH连接断开");
      ssh.con = undefined;
      ssh.shell = defShell;
      ssh.upFile = defUpFile;
      reject();
    });
  });
}
export async function defUpFile(fullPath: string, remoteFilePath?: string): Promise<void> {
  try {
    await runSSH();
    return await ssh.upFile(fullPath, remoteFilePath);
  } catch (_) {
    console.error("Upload 建立ssh连接错误", _);
    // return await defUpFile(fullPath);
  }
}
export async function defShell(commond: string): Promise<string> {
  try {
    await runSSH();
    return await ssh.shell(commond);
  } catch (_) {
    console.error("Shell 建立ssh连接错误", _);
  }
  return "Shell 建立ssh连接错误";
}
export function apicu(/** 文件完整路径 */p: string) {
  // console.log("src文件改变", remotePath + p.substring(root.length).replace(/\\/g, "/"));
  ssh.upFile(p);
}
export function apid(/** 文件完整路径 */p: string) {
  ssh.shell("rm -f " + remotePath + p.substring(root.length).replace(/\\/g, "/"));
}
